package thread.wang_bao_ling.c20;

import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * @Auther: luobiao
 * @Date: 2021/3/19 08:17
 * @Description:
 * 该队列中的元素需要实现Delayed接口，该接口中有个getDelay方法用于获取元素的延时时间，而且该接口实现了Comparable接口。
 * 因为内部是通过一个PriorityQueue队列进行存储元素（和上面的PriorityBlockingQueue类似，只是没有处理线程同步）。
 * 在每次添加元素的到PriorityQueue中的时候PriorityQueue里面会重新计算优先级，然后执行available.signal()
 * 唤醒阻塞在available条件变量上线程重新获取PriorityQueue中最先的元素设计阻塞时间
 * 相关链接：
 * {@link PriorityBlockingQueueTest}
 * @see <a href="https://www.processon.com/view/link/5f0f8651e0b34d44f04b2159">DelayQueue</a>
 */
public class DelayQueueTest {
    public static void main(String[] args) throws InterruptedException {
        DelayQueue<Message> queue = new DelayQueue<>();
        //记录添加时的系统当前时间
        long now = System.currentTimeMillis();

        // 启动一个消费线程
        new Thread(()->{
            while (true) {
                try {
                    queue.take();
                    //打印出取出元素的延时时间
                    System.out.println(System.currentTimeMillis() - now);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        // 添加5个元素到队列中
        queue.add(new Message(now + 8010));
        queue.add(new Message(now + 1000));
        TimeUnit.SECONDS.sleep(1);
        //一秒钟之后再添加一个延时2s的任务（其实相当延时一秒，因为now是上面休眠1s前的时间），每次add都会唤醒延时队列中在available条件上的线程重新计算阻塞时间
        queue.add(new Message( now+2000));
    }

    public static class Message implements Delayed {
        long deadline;

        public Message(long deadline) {
            this.deadline = deadline;
        }

        @Override
        public long getDelay(TimeUnit unit) {
            return deadline - System.currentTimeMillis();
        }

        @Override
        public int compareTo(Delayed o) {
            return (int) (getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
        }

        @Override
        public String toString() {
            return String.valueOf(deadline);
        }
    }
}

